package edu.uoc.vmerinom.tfc.service;

import java.util.Date;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.Query;

import org.jboss.seam.ScopeType;
import org.jboss.seam.annotations.In;
import org.jboss.seam.annotations.Logger;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Scope;
import org.jboss.seam.annotations.Transactional;
import org.jboss.seam.log.Log;

import edu.uoc.vmerinom.tfc.domain.Componente;
import edu.uoc.vmerinom.tfc.domain.Grupo;
import edu.uoc.vmerinom.tfc.domain.Usuario;

/**
 * Clase de servicios con las operaciones de acceso a base de datos para el
 * módulo de componentes.
 * 
 * @author Víctor Manuel Merino Martínez
 * 
 */
@Name("componentesService")
@Scope(ScopeType.CONVERSATION)
public class ComponentesService {
	/**
	 * Gestor de entidades.
	 */
	@In(create = true)
	EntityManager entityManager;

	/**
	 * Log para la trazabilidad de errores.
	 */
	@Logger
	Log log;

	/**
	 * Devuelve un listado de componentes ordenados por grupo, y nombre y
	 * apellidos del componente y actividad realizada.
	 * 
	 * @return Listado de componentes
	 */
	@SuppressWarnings("unchecked")
	public List<Componente> obtenerComponentes() {
		if (log.isDebugEnabled()) {
			log.debug("ComponentesService.obtenerComponentes()");
		}
		StringBuffer query = new StringBuffer("from Componente c left join fetch c.grupo left join fetch c.usuario ");
		query.append("order by c.grupo.nombre, c.usuario.nombre, c.usuario.primerApellido, c.usuario.segundoApellido desc");
		log.info(query);
		return entityManager.createQuery(query.toString()).getResultList();
	}

	/**
	 * Devuelve un listado de componentes vigentes a fecha de hoy y ordenados
	 * por grupo, y nombre y apellidos del componente y actividad realizada.
	 * 
	 * @return Listado de componentes
	 */
	@SuppressWarnings("unchecked")
	public List<Componente> obtenerComponentesActivos() {
		if (log.isDebugEnabled()) {
			log.debug("ComponentesService.obtenerComponentesActivos()");
		}
		StringBuffer query = new StringBuffer("from Componente c left join fetch c.grupo left join fetch c.usuario where c.baja > :currentDate ");
		query.append("order by c.grupo.nombre, c.usuario.nombre, c.usuario.primerApellido, c.usuario.segundoApellido desc");
		log.info(query);
		return entityManager.createQuery(query.toString()).setParameter("currentDate", new Date()).getResultList();
	}

	/**
	 * Devuelve un listado de grupos ordenados por nombre. Si el identificador
	 * de usuario que recibe por parámetros es distinto de <code>null</code>, se
	 * filtrará el listado mostrando los grupos que no tengan como componente a
	 * ese usuario.
	 * 
	 * @param id
	 *            Identificador del usuario
	 * @return Listado de grupos ordenados por nombre
	 */
	@SuppressWarnings("unchecked")
	public List<Grupo> obtenerGrupos(Integer id) {
		if (log.isDebugEnabled()) {
			log.debug("ComponentesService.obtenerGrupos(id:" + id + ")");
		}

		StringBuffer queryString = new StringBuffer("select distinct g from Grupo g left join fetch g.componentes");
		if (id != null) {
			queryString.append(" where g not in (select c.grupo from Componente c where c.usuario.id = :id)");
		}
		queryString.append(" order by g.nombre");

		log.info(queryString);
		Query query = entityManager.createQuery(queryString.toString());
		if (id != null) {
			query.setParameter("id", id);
		}

		return query.getResultList();
	}

	/**
	 * Devuelve un listado de grupos ordenados por nombre y filtrados por la
	 * fecha de baja para recuperar sólo los grupos vigentes a fecha de hoy. Si
	 * el identificador de usuario que recibe por parámetros es distinto de
	 * <code>null</code>, se filtrará el listado mostrando los grupos que no
	 * tengan como componente a ese usuario.
	 * 
	 * @param id
	 *            Identificador del usuario
	 * @return Listado de grupos ordenados por nombre
	 */
	@SuppressWarnings("unchecked")
	public List<Grupo> obtenerGruposActivos(Integer id) {
		if (log.isDebugEnabled()) {
			log.debug("ComponentesService.obtenerGruposActivos(id:" + id + ")");
		}

		StringBuffer queryString = new StringBuffer(
				"select distinct g from Grupo g left join fetch g.componentes where (g.baja is null or g.baja > :currentDate)");
		if (id != null) {
			queryString.append(" and g not in (select c.grupo from Componente c where c.usuario.id = :id and (c.baja is null or c.baja > :currentDate))");
		}
		queryString.append(" order by g.nombre");

		log.info(queryString);
		Query query = entityManager.createQuery(queryString.toString()).setParameter("currentDate", new Date());
		if (id != null) {
			query.setParameter("id", id);
		}

		return query.getResultList();
	}

	/**
	 * Devuelve un listado de grupos ordenados por nombre del grupo y filtrados
	 * por fecha de baja para obtener sólo los grupos vigentes a fecha de hoy.
	 * 
	 * @return Listado de grupos
	 */
	@SuppressWarnings("unchecked")
	public List<Grupo> obtenerGruposActivos() {
		if (log.isDebugEnabled()) {
			log.debug("ComponentesService.obtenerGruposActivos()");
		}
		String query = "select distinct g from Grupo g left join fetch g.componentes where (g.baja is null or g.baja > :currentDate) order by g.nombre";
		log.info(query);
		return entityManager.createQuery(query).setParameter("currentDate", new Date()).getResultList();
	}

	/**
	 * Devuelve un listado de grupos ordenados por nombre del grupo.
	 * 
	 * @return Listado de grupos
	 */
	@SuppressWarnings("unchecked")
	public List<Grupo> obtenerGrupos() {
		if (log.isDebugEnabled()) {
			log.debug("ComponentesService.obtenerGrupos()");
		}
		String query = "select distinct g from Grupo g left join fetch g.componentes order by g.nombre";
		log.info(query);
		return entityManager.createQuery(query).getResultList();
	}

	/**
	 * Devuelve un listado de los usuarios activos a fecha de hoy. El listado
	 * estará ordenado por nombre y apellidos del usuario. El método recibe un
	 * identificador de grupo. Si este identificador es distinto de
	 * <code>null</code>, filtrará el listado, mostrando los usuarios que no
	 * sean componentes del grupo cuyo identificador coincida.
	 * 
	 * @param id
	 *            Identificador del grupo
	 * @return Listado de usuarios
	 */
	@SuppressWarnings("unchecked")
	public List<Usuario> obtenerUsuariosActivos(Integer id) {
		if (log.isDebugEnabled()) {
			log.debug("ComponentesService.obtenerUsuariosActivos(id:" + id + ")");
		}
		StringBuffer queryString = new StringBuffer(
				"select distinct u from Usuario u left join fetch u.componentes where (u.baja is null or u.baja > :currentDate)");
		if (id != null) {
			queryString.append(" and u not in (select c.usuario from Componente c where c.grupo.id = :id and (c.baja is null or c.baja > :currentDate))");
		}
		queryString.append(" and u.perfil.administrador != true order by u.nombre, u.primerApellido, u.segundoApellido");

		log.info(queryString);
		Query query = entityManager.createQuery(queryString.toString()).setParameter("currentDate", new Date());
		if (id != null) {
			query.setParameter("id", id);
		}

		return query.getResultList();
	}

	/**
	 * Devuelve un listado de usuarios. El listado estará ordenado por nombre y
	 * apellidos del usuario. El método recibe un identificador de grupo. Si
	 * este identificador es distinto de <code>null</code>, filtrará el listado,
	 * mostrando los usuarios que no sean componentes del grupo cuyo
	 * identificador coincida.
	 * 
	 * @param id
	 *            Identificador del grupo
	 * @return Listado de usuarios
	 */
	@SuppressWarnings("unchecked")
	public List<Usuario> obtenerUsuarios(Integer id) {
		if (log.isDebugEnabled()) {
			log.debug("ComponentesService.obtenerUsuarios(id:" + id + ")");
		}
		StringBuffer queryString = new StringBuffer("select distinct u from Usuario u left join fetch u.componentes where u.perfil.administrador != true");
		if (id != null) {
			queryString.append(" and u not in (select c.usuario from Componente c where c.grupo.id = :id)");
		}
		queryString.append(" order by u.nombre, u.primerApellido, u.segundoApellido");

		log.info(queryString);
		Query query = entityManager.createQuery(queryString.toString());
		if (id != null) {
			query.setParameter("id", id);
		}

		return query.getResultList();
	}

	/**
	 * Actualiza el estado del grupo para poder visualizar los datos actuales de
	 * base de datos.
	 * 
	 * @param grupo
	 *            Grupo a actualizar
	 */
	public void actualizarEstadoGrupo(Grupo grupo) {
		if (log.isDebugEnabled()) {
			log.debug("ComponentesService.actualizarEstadoGrupo(grupo:" + grupo + ")");
		}
		if (grupo != null && grupo.getId() != null) {
			entityManager.refresh(grupo);
			if (grupo.getComponentes() != null && !grupo.getComponentes().isEmpty()) {
				if (log.isDebugEnabled()) {
					log.debug("Actualizamos el estado de los componentes del grupo...");
				}
				for (Componente componente : grupo.getComponentes()) {
					entityManager.refresh(componente);
				}
			}
		}
	}

	/**
	 * Inserta o modifica los datos del componente que recibe por parámetros.
	 * 
	 * @param componente
	 *            Componente a insertar/modificar
	 */
	@Transactional
	public void insertarModificarComponente(Componente componente) {
		if (log.isDebugEnabled()) {
			log.debug("ComponentesService.insertarModificarComponente(componente:" + componente + ")");
		}
		if (componente != null) {
			final Date hoy = new Date(System.currentTimeMillis());
			if (componente.getId() == null) {
				componente.setCreado(hoy);
			}
			componente.setModificado(hoy);
			entityManager.persist(componente);
			entityManager.refresh(componente.getGrupo());
		}
		entityManager.flush();
	}

	/**
	 * Inserta o modifica los datos de cada componente del listado que recibe
	 * por parámetros.
	 * 
	 * @param componentes
	 *            Listado de componente a insertar/modificar
	 */
	@Transactional
	public void insertarModificarComponentes(List<Componente> componentes) {
		if (log.isDebugEnabled()) {
			log.debug("ComponentesService.insertarModificarComponentes(componentes:" + componentes + ")");
		}
		if (componentes != null && !componentes.isEmpty()) {
			final Date hoy = new Date(System.currentTimeMillis());
			for (Componente componente : componentes) {
				if (componente.getId() == null) {
					componente.setCreado(hoy);
				}
				componente.setModificado(hoy);
				entityManager.persist(componente);
				entityManager.refresh(componente.getGrupo());
			}
		}
		entityManager.flush();
	}
}
